<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>IM:支持百万在线用户</title>
</head>
<body>
<div id="wrap" style="height:600px;">
    <div class="unit">
        <label>用户名:</label>
        <input id="username" value="a"/>
        <label style="width: 60px;">密码:</label>
        <input id="password" value="123"/>
        <input onclick="connect();" type="button" value="登录"/>
        <input onclick="disConnect();" type="button" value="退出"/>
        <div id="login_user"
             style="line-height: 25px;margin: 5px 5px 0 5px;padding-left:15px;cursor:pointer;display: none;">

        </div>
    </div>
    <div class="unit">
        <label>心跳:</label>
        <input type="button" value="心跳命令" onclick="heartbeatCmd();"/>
        <input id="heard_beat_time" style="width:100px;" placeholder="请输入心跳间隔" value="30000"/>
        <input type="button" value="开启定时心跳" onclick="openHeartbeat();"/>
        <input type="button" value="关闭定时心跳" onclick="closeHeartbeat();"/>
    </div>
    <div class="unit">
        <label>聊天记录:</label>
        <input type="button" value="清空记录" onclick="clearLogs();"/>
<!--        <input type="button" value="获取指定好友历史消息" onclick="friendHistoryCmd();" style="width: 150px;"/>-->
<!--        <input id="history_friend_id" style="width:100px;" placeholder="输入好友ID"/>-->
<!--        <input type="button" value="获取指定群组历史消息" onclick="groupHistoryCmd();" style="width: 150px;"/>-->
<!--        <input id="history_group_id" style="width:100px;" placeholder="输入群组ID"/>-->
    </div>
    <div class="unit">
        <label>群组:</label>
        <input id="room_name" style="width: 100px;" placeholder="群组名称"/>
        <input type="button" value="创建群组" onclick="createGroup();"/>
        <label>群组用户:</label>
        <input id="room_user_id" style="width: 100px;" placeholder="群组名称"/>
        <input type="button" value="邀请加入群组" onclick="invitationAddGroup();"/>
        <label>用户群组：</label>
        <div id="groups" style="display: inline"></div>
    </div>
    <div class="content">
        <div id="logs" class="unit log">
        </div>
        <div id="onlinePanel" class="online">
        </div>
    </div>
    <div class="unit">
        <label>输入内容:</label>
        <input id="content" style="width:500px;" onkeydown="keyDown(event)"/>
        <input onclick="sendGroup()" id="sendGroupBtn" type="button" value="群聊"/>&nbsp;
<!--        <input onclick="send()"-->
<!--                                                                                              id="sendBtn" type="button"-->
<!--                                                                                              value="私聊"/>-->
    </div>
</div>
</body>
<script>
    let socket;
    let username;
    let logDiv;
    let curUser;
    let nowGroupId;
    let t;

    function connect() {
        if (curUser) {
            alert("当前已登录 退出后重试！")
            return;
        }
        username = document.getElementById("username").value;
        const password = document.getElementById("password").value;
        logDiv = document.getElementById('logs');
        socket = new WebSocket("ws://localhost:9326?account=" + username + "&password=" + password);
        socket.onopen = function (e) {

        };
        socket.onerror = function (e) {

        };
        socket.onmessage = function (e) {
            let data = eval("(" + e.data + ")");
            switch (data.command) {
                // 登录返回信息
                case 6:
                    COMMAND_LOGIN_RESP(data);
                    break;
                // 加入群组通知
                case 9:
                    COMMAND_JOIN_GROUP_NOTIFY_RESP(data);
                    break;
                // 聊天请求
                case 11:
                    COMMAND_CHAT_RESP(data);
                    break;
                // 心跳响应
                case 13:
                    COMMAND_HEARTBEAT_RESP();
                    break;
                // 获取用户信息相应
                case 18:
                    COMMAND_GET_USER_RESP(data);
                    break;
                // 获取历史消息
                case 20:
                    COMMAND_GET_MESSAGE_RESP(data);
                    break;
                // 用户上下线消息
                case 21:
                    COMMAND_USER_STATUS_RESP(data);
                    break
                // 群组创建成功
                case 23:
                    COMMAND_CREATE_GROUP_RESP(data);
                    break
                // 消息已读响应
                case 25:
                    COMMAND_MESSAGE_READ_RESP(data);
                    break;
                default:
                    break;
            }
        };
        socket.onclose = function (e) {
            curUser = null
            document.getElementById('login_user').style.display = 'none'
            document.getElementById('groups').innerHTML = ""
            document.getElementById("onlinePanel").innerHTML = ""
            closeHeartbeat();
            log('green', '退出连接...')
        };
    }

    function disConnect() {
        socket.close()
    }

    function log(color, data, hasEnter) {
        logDiv.innerHTML += "<p style='font-size: 12px; color: " + color + ";'>" + data + "</p>"
        if (hasEnter) {
            logDiv.innerHTML += "<br />"
        }
        document.getElementById('logs').scrollTop = logDiv.scrollHeight;
    }

    function send(data) {
        const param = JSON.stringify(data)
        socket.send(param)
    }

    function keyDown(e) {
        let ev = window.event || e;
        //13是键盘上面固定的回车键
        if (ev.keyCode === 13) {
            sendGroup();
        }
    }

    function clearLogs () {
        const logDiv = document.getElementById('logs');
        logDiv.innerHTML = "";
    }

    function getId() {
        return Math.random().toString(36).substr(2) + Date.now().toString(36);
    }
    
    function sendGroup() {
        let content = document.getElementById('content').value;
        if (content === "")
            return;
        let param = {
            senderId: username,
            cmd: 11,
            roomId: nowGroupId,
            content: content,
        }
        send(param)
        document.getElementById('content').value = ''
    }

    function clearUnReadMessage() {
        let param = {
            cmd: 24,
            roomId: nowGroupId
        }
        send(param)
    }

    function changeGroup(id) {
        nowGroupId = id;
        clearUnReadMessage()
        for (let groupKey in curUser.groups) {
            let group = curUser.groups[groupKey]
            if (group.roomId === id) {
                let users = group.users;
                let onlineUserStr = "&nbsp;" + group.roomName + "在线成员(" + users.length + "/" + users.length + ")";
                for (var u = 0; u < users.length; u++) {
                    var user = users[u];
                    onlineUserStr += "<div id=\"" + user._id + "\" style=\"line-height: 25px;margin: 5px 5px 0 5px;padding-left:15px;cursor:pointer;\" onclick=\"onlineDb(this);\" onmouseover=\"onlineMove(this);\"  onmouseleave=\"onlineLeave(this);\"><img alt=\"" + user._id + "\" src=\"" + user.avatar + "\" height=\"25px\" width=\"25px;\" style=\"float:left\">&nbsp;<font size='2'>" + user.username + "(" + user._id + ")" + "-" + user.status.state + "</font></div>";
                }
                document.getElementById("onlinePanel").innerHTML = onlineUserStr;
            }
        }
        clearLogs();
        getHistoryMessage();
    }

    function getHistoryMessage() {
        let param = {
            cmd: 19,
            roomId: nowGroupId
        }
        send(param)
    }

    function createGroup() {
        let roomName = document.getElementById('room_name').value;
        let param = {
            cmd: 22,
            roomName: roomName,
        }
        send(param)
    }

    function flushGroup() {
        const groups = document.getElementById('groups');
        groups.innerHTML = ""
        for (let i = 0; i < curUser.groups.length; i++) {
            let str = ""
            if(curUser.groups[i].unreadCount && curUser.groups[i].unreadCount !== 0){
                str = "("+curUser.groups[i].unreadCount+")"
            }
            groups.innerHTML += "<input type='button' style='margin-right: 5px;' id='" + curUser.groups[i].roomId + "' value='" + curUser.groups[i].roomName + str + "' onclick=\"changeGroup('" + curUser.groups[i].roomId + "')\" />"
        }
    }

    function heartbeatCmd() {
        if (curUser) {
            COMMAND_HEARTBEAT_REQ();
        }
    }

    function openHeartbeat() {
        if (t) {
            alert("定时心跳已开启,勿重复开启")
        }
        let time = document.getElementById('heard_beat_time').value
        if (time) {
            t = setInterval(() => {
                if (curUser) {
                    COMMAND_HEARTBEAT_REQ()
                } else {
                    clearInterval(t)
                }
            }, time)
            log('green', '定时心跳已开启...')
        } else {
            alert("请输入正确的间隔!")
        }
    }

    function closeHeartbeat() {
        if (t) {
            clearInterval(t)
            log('green', '定时心跳已关闭...')
        }
        t = null
    }

    function invitationAddGroup() {
        let userId = document.getElementById('room_user_id').value;
        if (!userId) {
            alert("请输入用户ID")
        }
        let param = {
            cmd: 7,
            group: {
                roomId: nowGroupId,
            },
            users: [{
                _id: userId
            }],
            code: 1
        }
        send(param);
    }

    // 登录成功返回
    function COMMAND_LOGIN_RESP(data) {
        if (data.success) {
            username = data.data._id
            log('green', '连接成功...')
            let param = {
                cmd: 17,
                userId: username,
            }
            send(param)

        }
    }

    // 聊天消息响应
    function COMMAND_CHAT_RESP(data) {
        let chatObj = data.data;
        let createTime = chatObj.date + ' ' + chatObj.timestamp;
        let content = chatObj.content;
        let inner = chatObj.username  + " " + createTime
        if(chatObj.roomId === nowGroupId){
            // 如果是当前组ID,
            clearUnReadMessage()
            log('#009ACD',inner)
            log('#FFFFFF',content)
            return ;
        }
        for (let i = 0; i < curUser.groups.length; i++) {
            let group = curUser.groups[i];
            if(group.roomId === chatObj.roomId){
                group.unreadCount = chatObj.unreadCount
            }
        }
        flushGroup()
    }

    // 获取用户信息响应
    function COMMAND_GET_USER_RESP(data) {
        curUser = data.data
        const loginUser = document.getElementById('login_user')
        loginUser.style.display = 'inline'
        // 展示用户信息
        loginUser.innerHTML = "<img alt=\"" + curUser._id + "\" src=\"" + curUser.avatar + "\" height=\"15px\" width=\"15px;\" >&nbsp;<font size='2'>" + curUser.username + "(" + curUser._id + ")" + "-" + curUser.status.state + "</font>"
        // 更新群组BUTTON
        if (curUser.groups.length > 0) {
            let group = curUser.groups[0];
            nowGroupId = group.roomId;
            let users = group.users;
            let onlineUserStr = "&nbsp;" + group.roomName + "在线成员(" + users.length + "/" + users.length + ")";
            for (var u = 0; u < users.length; u++) {
                var user = users[u];
                onlineUserStr += "<div id=\"" + user._id + "\" style=\"line-height: 25px;margin: 5px 5px 0 5px;padding-left:15px;cursor:pointer;\" onclick=\"onlineDb(this);\" onmouseover=\"onlineMove(this);\"  onmouseleave=\"onlineLeave(this);\"><img alt=\"" + user._id + "\" src=\"" + user.avatar + "\" height=\"25px\" width=\"25px;\" style=\"float:left\">&nbsp;<font size='2'>" + user.username + "(" + user._id + ")" + "-" + user.status.state + "</font></div>";
            }
            document.getElementById("onlinePanel").innerHTML = onlineUserStr;
            flushGroup()
            getHistoryMessage()
            clearUnReadMessage()
        }
    }

    // 历史消息处理
    function COMMAND_GET_MESSAGE_RESP(data) {
        let messages = data.data
        for (let message of messages) {
            let createTime = message.date + ' ' + message.timestamp;
            let content = message.content;
            let inner = message.username  + " " + createTime
            log('#009ACD',inner)
            log('#FFFFFF',content)
        }
    }

    // 加入群聊返回
    function COMMAND_JOIN_GROUP_NOTIFY_RESP(data) {
        let group = data.data.group
        let find = false
        for (let groupsKey in curUser.groups) {
            let groupTemp = curUser.groups[groupsKey]
            if (groupTemp.roomId === group.roomId) {
                find = true
                groupTemp.users = group.users
            }
        }

        if(find === false){
            curUser.groups.push(group)
            const groups = document.getElementById('groups');
            groups.innerHTML += "<input type='button' style='margin-right: 5px;' id='" + group.roomId + "' value='" + group.roomName + "' onclick='changeGroup(" + group.roomId + ")' />"

        }

        changeGroup(group.roomId)
        log('red', data.data.message)
    }

    // 用户上下线消息
    function COMMAND_USER_STATUS_RESP(data) {
        let group = data.data.group
        if(data.data.user.status.state === 'ONLINE'){
            log('green',group.roomName + ' 的' + data.data.user.username + '已上线...')
        }else{
            log('red',group.roomName + ' 的' + data.data.user.username + '已下线...')
        }

        for (let i = 0; i < curUser.groups.length; i++) {
            let g = curUser.groups[i]
            if (g.roomId === group.roomId) {
                curUser.groups[i] = {...group}
                if (g.roomId === nowGroupId) {
                    let users = curUser.groups[i].users
                    let onlineUserStr = "&nbsp;" + group.roomName + "在线成员(" + users.length + "/" + users.length + ")";
                    for (let i = 0; i < users.length; i++) {
                        let user = users[i];
                        onlineUserStr += "<div id=\"" + user._id + "\" style=\"line-height: 25px;margin: 5px 5px 0 5px;padding-left:15px;cursor:pointer;\" onclick=\"onlineDb(this);\" onmouseover=\"onlineMove(this);\"  onmouseleave=\"onlineLeave(this);\"><img alt=\"" + user._id + "\" src=\"" + user.avatar + "\" height=\"25px\" width=\"25px;\" style=\"float:left\">&nbsp;<font size='2'>" + user.username + "(" + user._id + ")" + "-" + user.status.state + "</font></div>";
                    }
                    document.getElementById("onlinePanel").innerHTML = onlineUserStr;
                }
            }
        }

    }

    // 群组创建成功
    function COMMAND_CREATE_GROUP_RESP(data) {
        let group = data.data
        curUser.groups.push(group)
        const groups = document.getElementById('groups');
        groups.innerHTML += "<input type='button' style='margin-right: 5px;' id='" + group.roomId + "' value='" + group.roomName + "' onclick='changeGroup(" + group.roomId + ")' />"

        changeGroup(group.roomId)
        log('green', "群组创建成功消息返回.....")
    }

    // 发送心跳
    function COMMAND_HEARTBEAT_REQ() {
        let heartbeatCmd = {
            cmd: 13,
            hbbyte: "-127"
        }
        send(heartbeatCmd);
        log('green', "心跳请求...")
    }

    // 心跳响应
    function COMMAND_HEARTBEAT_RESP() {
        log('green', "心跳响应...")
    }

    // 消息已读回执
    function COMMAND_MESSAGE_READ_RESP(data) {
        let groupId = data.data.roomId
        for (let i = 0; i < curUser.groups.length; i++) {
            let group = curUser.groups[i];
            if(group.roomId === groupId){
                group.unreadCount = 0
            }
        }
        flushGroup()
    }

</script>
<style media="screen">
    * {
        margin: 0;
        padding: 0;
    }

    #wrap {
        margin: 10px auto;
        width: 80%;
        height: 99%;
    }

    .unit {
        margin-bottom: 5px;
        padding: 5px;
        border: solid 1px #000;
        height: auto;
        clear: both;
        box-sizing: border-box;
    }

    .unit label {
        text-align: right;
        width: 100px;
        line-height: 30px;
        display: inline-block;
    }

    .unit input {
        line-height: 30px;
        width: 100px;
        height: 30px;
    }

    .log {
        font-size: 11px;
        font-family: Courier;
        overflow: auto;
        height: 100%;
        background: black;
        width: 85%;
        float: left
    }

    .log p {
        padding: 2px;
        margin: 0;
    }

    .content {
        width: 100%;
        overflow: hidden;
        height: 80%;
        margin: 0 0 5px 0;
    }

    .online {
        width: 15%;
        height: 100%;
        float: right;
        background: #FCFCFC;
        border: solid 1px #000;
        box-sizing: border-box;
    }
</style>
</html>
